package com.lovegu.three;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * <p>
 * 解决哲学家就餐问题
 * </p>
 *
 * @author 老顾
 * @since 2023/4/4
 */
public class DiningPhilosophers {

    // 哲学家的数量
    private static final int N = 5;
    // 哲学家的状态
    private static final int THINKING = 0;
    private static final int HUNGRY = 1;
    private static final int EATING = 2;
    // 哲学家的状态数组
    private final int[] state = new int[N];
    // 哲学家使用的筷子
    private final Lock[] chopsticks = new ReentrantLock[N];

    public DiningPhilosophers() {
        for (int i = 0; i < N; i++) {
            state[i] = THINKING;
            chopsticks[i] = new ReentrantLock();
        }
    }

    // 哲学家尝试获取左右两边的筷子
    private void takeChopsticks(int philosopher) {
        chopsticks[philosopher].lock();
        chopsticks[(philosopher + 1) % N].lock();
        state[philosopher] = EATING;
        System.out.println("哲学家 " + philosopher + " 正在就餐");
    }

    // 哲学家放下左右两边的筷子
    private void putChopsticks(int philosopher) {
        chopsticks[philosopher].unlock();
        chopsticks[(philosopher + 1) % N].unlock();
        state[philosopher] = THINKING;
        System.out.println("哲学家 " + philosopher + " 放下筷子");
    }

    // 哲学家的行为
    public void philosopherBehavior(int philosopher) throws InterruptedException {
        while (true) {
            // 哲学家思考一段时间
            Thread.sleep((long) (Math.random() * 10000));
            // 哲学家变得饿了
            state[philosopher] = HUNGRY;
            System.out.println("哲学家 " + philosopher + " 变得饿了");
            // 哲学家尝试获取左右两边的筷子
            takeChopsticks(philosopher);
            // 哲学家吃完饭放下左右两边的筷子
            putChopsticks(philosopher);
        }
    }

    public static void main(String[] args) {
        DiningPhilosophers dp = new DiningPhilosophers();
        Thread[] threads = new Thread[N];
        for (int i = 0; i < N; i++) {
            final int philosopher = i;
            threads[i] = new Thread(() -> {
                try {
                    dp.philosopherBehavior(philosopher);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            threads[i].start();
        }
    }
}
